package com.eas.biz.service.statistic.impl;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.eas.biz.constants.BizConstants;
import com.eas.biz.dao.statistic.dataobject.BaseGradeStatisticDO;
import com.eas.biz.dao.statistic.dataobject.ClassGradeStatisticDO;
import com.eas.biz.dao.statistic.dataobject.ClassStatisticDO;
import com.eas.biz.dao.statistic.dataobject.ClassStatisticQuery;
import com.eas.biz.dao.statistic.interfaces.ClassGradeStatisticDao;
import com.eas.biz.dao.statistic.interfaces.ClassStatisticDao;
import com.eas.biz.service.statistic.domain.ClassStatistic;
import com.eas.biz.service.statistic.domain.ClassSubjectStatistic;
import com.eas.biz.service.statistic.domain.GradeLevelCount;
import com.eas.biz.service.statistic.interfaces.ClassStatisticService;
import com.eas.biz.service.statistic.query.StatisticServiceQuery;
import com.eas.biz.service.user.interfaces.ClassService;

@Service("classStatisticService")
public class ClassStatisticServiceImpl implements ClassStatisticService {

    @Resource(name = "classCacheService")
    private ClassService classService;

    @Autowired
    private ClassStatisticDao classStatisticDao;

    @Autowired
    private ClassGradeStatisticDao classGradeStatisticDao;

    public List<ClassStatistic> getStatisticList(long schoolId, long examId,
            StatisticServiceQuery queryInput) {
        List<ClassStatistic> csList = new LinkedList<ClassStatistic>();
        ClassStatisticQuery query = new ClassStatisticQuery(queryInput);
        query.setSchoolId(schoolId);
        query.setExamId(examId);
        boolean withSubjectStatistic = queryInput != null && queryInput.isWithSubjectStatistic();
        boolean withGradeStatistic = queryInput != null && queryInput.isWithGradeStatistic();

        if (withSubjectStatistic) {
            List<ClassStatisticDO> list = classStatisticDao.selectByQuery(query);
            if (list != null) {
                Map<Long, ClassStatistic> classMap = new HashMap<Long, ClassStatistic>();
                for (ClassStatisticDO csDO : list) {
                    ClassStatistic cs = classMap.get(csDO.getClassId());
                    if (cs == null) {
                        cs = buildClassStatistic(csDO, false);
                        classMap.put(csDO.getClassId(), cs);
                    }

                    if (BizConstants.TOTAL_SCORE_SUBJECT.equals(csDO.getSubject())) {
                        ClassStatistic newStatistic = buildClassStatistic(csDO, withGradeStatistic);
                        newStatistic.setSubjectMap(cs.getSubjectMap());
                        classMap.put(csDO.getClassId(), newStatistic);
                    } else {
                        ClassSubjectStatistic css = buildSubjectStatistic(csDO, withGradeStatistic);
                        cs.addSubjectStatistic(css);
                    }
                }

                csList.addAll(classMap.values());
            }
        } else {
            query.setSubject(BizConstants.TOTAL_SCORE_SUBJECT);

            List<ClassStatisticDO> list = classStatisticDao.selectByQuery(query);
            if (list != null) {
                for (ClassStatisticDO csDO : list) {
                    ClassStatistic cs = buildClassStatistic(csDO, withGradeStatistic);
                    csList.add(cs);
                }
            }
        }

        return csList;
    }

    public List<ClassSubjectStatistic> getSubjectStatisticList(long schoolId, long examId,
            String subject, StatisticServiceQuery queryInput) {
        List<ClassSubjectStatistic> list = new LinkedList<ClassSubjectStatistic>();
        if (StringUtils.isBlank(subject)) {
            return list;
        }
        boolean withGradeStatistic = queryInput != null && queryInput.isWithGradeStatistic();
        ClassStatisticQuery query = new ClassStatisticQuery(queryInput);
        query.setSchoolId(schoolId);
        query.setExamId(examId);
        query.setSubject(subject);
        List<ClassStatisticDO> result = classStatisticDao.selectByQuery(query);
        if (result != null) {
            for (ClassStatisticDO csDO : result) {
                ClassSubjectStatistic cs = buildSubjectStatistic(csDO, withGradeStatistic);
                list.add(cs);
            }
        }
        return list;
    }

    public ClassStatistic getStatistic(long classId, long examId, StatisticServiceQuery queryInput) {
        ClassStatistic classStatistic = null;
        boolean withSubjectStatistic = queryInput != null && queryInput.isWithSubjectStatistic();
        boolean withGradeStatistic = queryInput != null && queryInput.isWithGradeStatistic();

        if (withSubjectStatistic) {
            ClassStatisticQuery query = new ClassStatisticQuery(queryInput);
            query.setClassId(classId);
            query.setExamId(examId);

            List<ClassStatisticDO> list = classStatisticDao.selectByQuery(query);
            if (list != null) {
                Map<String, ClassSubjectStatistic> subjectMap = new HashMap<String, ClassSubjectStatistic>();
                for (ClassStatisticDO csDO : list) {
                    if (BizConstants.TOTAL_SCORE_SUBJECT.equals(csDO.getSubject())) {
                        classStatistic = buildClassStatistic(csDO, withGradeStatistic);
                        continue;
                    }

                    ClassSubjectStatistic subject = subjectMap.get(csDO.getSubject());
                    if (subject == null) {
                        subject = buildSubjectStatistic(csDO, withGradeStatistic);
                        subjectMap.put(subject.getSubject(), subject);
                    }
                }

                if (classStatistic != null) {
                    classStatistic.setSubjectMap(subjectMap);
                }
            }
        } else {
            ClassSubjectStatistic subjectStatistic = getSubjectStatistic(classId, examId,
                    BizConstants.TOTAL_SCORE_SUBJECT, queryInput);
            if (subjectStatistic != null) {
                classStatistic = buildClassStatistic(subjectStatistic);
            }
        }
        return classStatistic;
    }

    public ClassSubjectStatistic getSubjectStatistic(long classId, long examId, String subject,
            StatisticServiceQuery queryInput) {
        if (StringUtils.isBlank(subject)) {
            return null;
        }

        boolean withGradeStatistic = queryInput != null && queryInput.isWithGradeStatistic();
        ClassStatisticQuery query = new ClassStatisticQuery(queryInput);
        query.setClassId(classId);
        query.setExamId(examId);
        query.setSubject(subject);
        List<ClassStatisticDO> list = classStatisticDao.selectByQuery(query);
        if (list != null && list.size() > 0) {
            ClassSubjectStatistic subjectStatistic = buildSubjectStatistic(list.get(0),
                    withGradeStatistic);
            return subjectStatistic;
        } else {
            return null;
        }
    }

    public List<ClassSubjectStatistic> getSubjectStatistic(List<Long> classIdList, long examId,
            String subject, StatisticServiceQuery queryInput) {
        List<ClassSubjectStatistic> list = new LinkedList<ClassSubjectStatistic>();
        if (StringUtils.isBlank(subject) || classIdList == null || classIdList.size() == 0) {
            return list;
        }

        boolean withGradeStatistic = queryInput != null && queryInput.isWithGradeStatistic();
        ClassStatisticQuery query = new ClassStatisticQuery(queryInput);
        query.setClassIdList(classIdList);
        query.setExamId(examId);
        query.setSubject(subject);
        List<ClassStatisticDO> result = classStatisticDao.selectByQuery(query);
        if (result != null) {
            for (ClassStatisticDO csDO : result) {
                list.add(buildSubjectStatistic(csDO, withGradeStatistic));
            }
        }
        return list;
    }

    public List<GradeLevelCount> getGradeList(long examId, long classId, String subject) {
        if (StringUtils.isBlank(subject)) {
            return null;
        }

        List<GradeLevelCount> gradeList = new LinkedList<GradeLevelCount>();
        ClassStatisticQuery query = new ClassStatisticQuery(null);
        query.setClassId(classId);
        query.setExamId(examId);
        query.setSubject(subject);
        List<ClassGradeStatisticDO> list = classGradeStatisticDao.selectByQuery(query);
        if (gradeList != null) {
            for (BaseGradeStatisticDO grade : list) {
                gradeList.add(buildGradeLevelCount(grade));
            }
        }
        return gradeList;
    }

    public List<GradeLevelCount> getGradeList(long examId, long classId) {
        return getGradeList(examId, classId, BizConstants.TOTAL_SCORE_SUBJECT);
    }

    private ClassStatistic buildClassStatistic(ClassSubjectStatistic subjectStatistic) {
        ClassStatistic cs = new ClassStatistic();
        cs.setClassId(subjectStatistic.getClassId());
        cs.setSchoolId(subjectStatistic.getSchoolId());
        cs.setExamId(subjectStatistic.getExamId());
        cs.setStudentCount(subjectStatistic.getStudentCount());
        cs.setAvgScore(subjectStatistic.getAvgScore());
        cs.setMaxScore(subjectStatistic.getMaxScore());
        cs.setMinScore(subjectStatistic.getMinScore());
        cs.setStandardDiviation(subjectStatistic.getStandardDiviation());
        cs.setGradeList(subjectStatistic.getGradeList());
        cs.setLevelMap(subjectStatistic.getLevelMap());
        cs.setClassInfo(subjectStatistic.getClassInfo());
        return cs;
    }

    private ClassStatistic buildClassStatistic(ClassStatisticDO statisticDO,
            boolean withGradeStatistic) {
        ClassStatistic cs = new ClassStatistic();
        cs.setClassId(statisticDO.getClassId());
        cs.setSchoolId(statisticDO.getSchoolId());
        cs.setExamId(statisticDO.getExamId());
        cs.setStudentCount(statisticDO.getStudentCount());
        cs.setAvgScore(statisticDO.getAvgScore());
        cs.setMaxScore(statisticDO.getMaxScore());
        cs.setMinScore(statisticDO.getMinScore());
        cs.setStandardDiviation(statisticDO.getDiviation());
        if (withGradeStatistic) {
            cs.setGradeList(getGradeList(cs.getExamId(), cs.getClassId(),
                    BizConstants.TOTAL_SCORE_SUBJECT));
            cs.buildLevelMap();
        }
        cs.setClassInfo(classService.getClassInfo(cs.getClassId()));
        return cs;
    }

    private GradeLevelCount buildGradeLevelCount(BaseGradeStatisticDO grade) {
        GradeLevelCount count = new GradeLevelCount();
        count.setLevel(grade.getLevel());
        count.setScore(grade.getScore());
        count.setCount(grade.getCount());
        return count;
    }

    private ClassSubjectStatistic buildSubjectStatistic(ClassStatisticDO classStatisticDO,
            boolean withGradeStatistic) {
        ClassSubjectStatistic subject = new ClassSubjectStatistic();
        subject.setClassId(classStatisticDO.getClassId());
        subject.setSchoolId(classStatisticDO.getSchoolId());
        subject.setExamId(classStatisticDO.getExamId());
        subject.setSubject(classStatisticDO.getSubject());
        subject.setStudentCount(classStatisticDO.getStudentCount());
        subject.setAvgScore(classStatisticDO.getAvgScore());
        subject.setMaxScore(classStatisticDO.getMaxScore());
        subject.setMinScore(classStatisticDO.getMinScore());
        subject.setStandardDiviation(classStatisticDO.getDiviation());
        if (withGradeStatistic) {
            subject.setGradeList(getGradeList(subject.getExamId(), subject.getClassId(),
                    subject.getSubject()));
            subject.buildLevelMap();
        }
        subject.setClassInfo(classService.getClassInfo(subject.getClassId()));
        return subject;
    }
}
